import shelljs from 'shelljs';
import fs from 'fs';
import path from 'path';
import cp from 'child_process';
import { fileURLToPath } from "node:url";

import { randomChars, add_servbuf } from '../utils.js';

const fp = fs.promises;
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);


const EXEC_TIMEOUT = 10 * 1000 /* 毫秒 */;


/**
 * 从字符串中提取出参数，包括带引号的参数（参数内部包含空格）和不带引号的参数。
 * 
*/
const processParams = (inputString) => {
    // 定义正则表达式用于匹配带引号的参数和不带引号的参数
    const regex = /(?:"([^"]*)")|(?:'([^']*)')|\S+/g;
    let match;
    const params = [];

    // 循环匹配所有参数
    while ((match = regex.exec(inputString)) !== null) {
        // 检查是否有带引号的参数，如果有，取第一个捕获组的内容
        const param = match[1] || match[2] || match[0];
        // 添加到参数数组中
        params.push(param);
    }

    return params;
};

export async function run(executable_path, exec_pramas = '', exec_timeout = EXEC_TIMEOUT) {
    console.log(`${executable_path} ${exec_pramas}`);
    return new Promise(async (rsv, _rjt) => {
        // const child = cp.spawn(`${executable_path}`.trim(), exec_pramas.split(' ').map(s => s.trim()).filter(s => !!s));
        const child = cp.spawn(`${executable_path}`.trim(), processParams(exec_pramas));

        const output = {
            stdout: '',
            stderr: ''
        };
        child.stdout.on('data', data => {
            output.stdout += data.toString();
        })

        child.stderr.on('data', data => {
            output.stderr += data.toString();
        })

        // 设置一个定时器，在exec_timeout秒后中断执行
        const timeout = setTimeout(() => {
            child.kill();
            console.log('执行被强制中断：', executable_path + ' ' + exec_pramas);
            rsv(output);
            clearTimeout(timeout);
        }, exec_timeout);

        let stdoutEnded = false, stderrEnded = false;
        function tryClosing() {
            if (stdoutEnded && stderrEnded) {
                rsv(output);
                clearTimeout(timeout);
            }
        }

        child.stdout.on('end', function () { stdoutEnded = true; tryClosing(); });
        child.stderr.on('end', function () { stderrEnded = true; tryClosing(); });
    })
}


export function c(code, compile_params, exec_pramas) {
    console.log('Run c code.');

    return new Promise(async (resolve, reject) => {
        const rc = randomChars();
        const file_name = 'cpp_' + rc + '.cpp';
        const target_name = 'cpp_' + rc;
        const c_code_dir = path.join(__dirname, '../../c_code');
        const source_path = path.join(__dirname, '../../c_code/', file_name);

        await fp.writeFile(source_path, code);
        // 在C语言文件中添加setvbuf函数调用，以解决输出缓冲区溢出的问题。
        await add_servbuf(source_path);

        shelljs.cd(c_code_dir);
        // const compile_output = shelljs.exec(`g++ ./${file_name} -o ${target_name} ${compile_params}`);
        let compile_output = await run('g++', `./${file_name} -w -o ${target_name} ${compile_params}`);

        // 如果g++运行错误，就使用gcc再试
        if (compile_output.stderr) {
            const file_name = 'cpp_' + rc + '.c';
            const source_path = path.join(__dirname, '../../c_code/', file_name);
            await fp.writeFile(source_path, code);
            compile_output = await run('gcc', `./${file_name} -w -o ${target_name} ${compile_params}`);
            shelljs.rm('-f', file_name);
        }


        if (compile_output.stderr) {
            reject(compile_output.stderr);
            shelljs.rm('-f', file_name);
            return;
        }

        const executable_path = path.join(c_code_dir, target_name);

        const output = await run(executable_path, exec_pramas);

        shelljs.rm('-f', executable_path + '*');

        if (output.stderr) {
            console.log(output);
            reject(output.stderr);
        } else {
            resolve(output.stdout);
        }
    })
}

export function rust(code, exec_params) {
    console.log('Run rust code.');
    return new Promise(async (resolve, reject) => {
        const rc = randomChars();

        const rust_code_dir = path.join(__dirname, '../../rust_code');
        const bin_path = path.join(__dirname, `../../rust_code/src/bin/rs_${rc}.rs`);

        // 警告的内容也会走错误输出，但实际并不影响代码执行
        // 这里在执行是忽略所有警告
        code = '#![allow(warnings)]\n\n' + code;
        await fp.writeFile(bin_path, code);
        shelljs.cd(rust_code_dir);

        const output = await run(`cargo`, `run -q --bin rs_${rc}${exec_params ? ' -- ' + exec_params : ''}`);

        shelljs.rm('-f', bin_path);
        shelljs.rm('-f', `${rust_code_dir}/target/debug/*${rc}*`);
        shelljs.rm('-f', `${rust_code_dir}/target/debug/deps/*${rc}*`);

        if (output.stderr && !output.stdout) {
            reject(output.stderr);
        } else {
            resolve(output.stdout);
        }
    })
}


export default {
    rust,
    c
}